import math
import re

import logzero
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import (
    QComboBox,
    QDoubleSpinBox,
    QGridLayout,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QPushButton,
    QRadioButton,
    QSpinBox,
    QSplitter,
    QTableWidget,
    QTableWidgetItem,
    QVBoxLayout,
    QWidget,
)
from devices.mengy_idin201f import ADJ_MEN_CONFS, PARA_CONFS, PRIMARY_MEA_CONFS, SECONDARY_MEA_CONFS
from helpers.mengy_bytes_helper import bytes2Int, bytes2Float32
from qt_custom_widget.qt_helper import addTooltipInSpinBox
from qt_custom_widget.qt_ScrollMessageBox import ScrollMessageBox

logger = logzero.setup_logger("tabs.10.idin201f")
RE_NUMBER = re.compile(r"\d+\.?\d+")


class Tab_10_IDIN201F(QWidget):
    def __init__(self, serRecvDir, sendQueuePack, logger=logger):
        super().__init__()
        self.serRecvDir = serRecvDir
        self.sendQueuePack = sendQueuePack
        self.logger = logger
        self.initUI()
        self.logger.info("this is tab10 idin201f logger")
        self.setObjectName("tab10")
        layout = QGridLayout(self)
        layout.addWidget(self.idin201fGroupBox, 0, 0, 1, 1)  # 测试报告

    def initUI(self):
        """初始化显示构件"""
        self.createIDIN201FGroupBox()

    def createIDIN201FGroupBox(self):
        """
        create tab for IDIN201F
        """
        self.idin201fGroupBox = QWidget()
        layout = QHBoxLayout(self.idin201fGroupBox)
        splitter = QSplitter(Qt.Horizontal)
        layout.addWidget(splitter)

        self.idin201fPrimaryGB = QGroupBox("一次值")
        subLayout = QVBoxLayout(self.idin201fPrimaryGB)
        self.idin201fPrimaryMeaTable = QTableWidget(len(PRIMARY_MEA_CONFS), 1)
        self.idin201fPrimaryMeaTable.setEditTriggers(QTableWidget.NoEditTriggers)
        self.idin201fPrimaryMeaTable.setHorizontalHeaderLabels(("测量值",))
        self.idin201fPrimaryMeaTable.setVerticalHeaderLabels((i.name for i in PRIMARY_MEA_CONFS))
        for i in range(self.idin201fPrimaryMeaTable.rowCount()):
            for j in range(self.idin201fPrimaryMeaTable.columnCount()):
                item = QTableWidgetItem("")
                item.setTextAlignment(Qt.AlignCenter)
                self.idin201fPrimaryMeaTable.setItem(i, j, item)
        self.idin201fPrimaryMeaTable.resizeRowsToContents()
        subLayout.addWidget(self.idin201fPrimaryMeaTable)
        splitter.addWidget(self.idin201fPrimaryGB)

        self.idin201fSecondaryGB = QGroupBox("二次值")
        subLayout = QVBoxLayout(self.idin201fSecondaryGB)
        self.idin201fSecondaryMeaTable = QTableWidget(len(SECONDARY_MEA_CONFS), 3)
        # self.idin201fSecondaryMeaTable.setEditTriggers(QTableWidget.NoEditTriggers)
        self.idin201fSecondaryMeaTable.setHorizontalHeaderLabels(("测量值", "标准值", "误差"))
        self.idin201fSecondaryMeaTable.setVerticalHeaderLabels((i.name for i in SECONDARY_MEA_CONFS))
        for i in range(self.idin201fSecondaryMeaTable.rowCount()):
            for j in range(self.idin201fSecondaryMeaTable.columnCount()):
                if j == 1 and i == 16:
                    item = QTableWidgetItem("50.00Hz")
                elif j >= 1 and i >= 29:
                    item = QTableWidgetItem("-------")
                else:
                    item = QTableWidgetItem("")
                item.setTextAlignment(Qt.AlignCenter)
                if j == 0 and i in (29, 30, 33, 34):
                    item.setFlags(item.flags() | Qt.ItemIsEditable)
                    item.setBackground(QColor("#91e87d"))
                else:
                    item.setFlags(item.flags() ^ Qt.ItemIsEditable)
                self.idin201fSecondaryMeaTable.setItem(i, j, item)
        self.idin201fSecondaryMeaTable.resizeRowsToContents()
        self.idin201fSecondaryMeaTableFreeze = None
        self.idin201fSecondaryMeaTable.cellDoubleClicked.connect(self.idin201fPreSetEngery)
        self.idin201fSecondaryMeaTable.cellChanged.connect(self.idin201fSetEngery)
        subLayout.addWidget(self.idin201fSecondaryMeaTable)

        tempLayout = QHBoxLayout()
        subLayout.addLayout(tempLayout)
        self.idin201fSecondaryIs = QDoubleSpinBox()
        self.idin201fSecondaryIs.setDecimals(3)
        self.idin201fSecondaryIs.setSingleStep(0.5)
        self.idin201fSecondaryIs.setSuffix("A")
        addTooltipInSpinBox(self.idin201fSecondaryIs, (0.0, 5000.0))
        self.idin201fSecondaryIs.setValue(5.00)
        tempLayout.addWidget(QLabel("额定电流"))
        tempLayout.addWidget(self.idin201fSecondaryIs)

        self.idin201fSecondaryUs = QDoubleSpinBox()
        self.idin201fSecondaryUs.setDecimals(2)
        self.idin201fSecondaryUs.setSingleStep(0.5)
        self.idin201fSecondaryUs.setSuffix("V")
        addTooltipInSpinBox(self.idin201fSecondaryUs, (0.0, 5000.0))
        self.idin201fSecondaryUs.setValue(220.0)
        tempLayout.addWidget(QLabel("额定电压"))
        tempLayout.addWidget(self.idin201fSecondaryUs)

        self.idin201fSecondaryRs = QDoubleSpinBox()
        self.idin201fSecondaryRs.setDecimals(1)
        self.idin201fSecondaryRs.setSingleStep(0.5)
        self.idin201fSecondaryRs.setSuffix("°")
        addTooltipInSpinBox(self.idin201fSecondaryRs, (0.0, 5000.0))
        self.idin201fSecondaryRs.setValue(60.0)
        tempLayout.addWidget(QLabel("U/I角度"))
        tempLayout.addWidget(self.idin201fSecondaryRs)

        tempLayout = QHBoxLayout()
        subLayout.addLayout(tempLayout)
        self.idin201fSecondaryIt = QDoubleSpinBox()
        self.idin201fSecondaryIt.setDecimals(1)
        self.idin201fSecondaryIt.setSingleStep(0.1)
        self.idin201fSecondaryIt.setSuffix("%")
        addTooltipInSpinBox(self.idin201fSecondaryIt, (0.0, 10.0))
        self.idin201fSecondaryIt.setValue(0.5)
        tempLayout.addWidget(QLabel("电流误差"))
        tempLayout.addWidget(self.idin201fSecondaryIt)

        self.idin201fSecondaryUt = QDoubleSpinBox()
        self.idin201fSecondaryUt.setDecimals(1)
        self.idin201fSecondaryUt.setSingleStep(0.1)
        self.idin201fSecondaryUt.setSuffix("%")
        addTooltipInSpinBox(self.idin201fSecondaryUt, (0.0, 10.0))
        self.idin201fSecondaryUt.setValue(0.5)
        tempLayout.addWidget(QLabel("电压误差"))
        tempLayout.addWidget(self.idin201fSecondaryUt)

        self.idin201fSecondaryRt = QDoubleSpinBox()
        self.idin201fSecondaryRt.setDecimals(3)
        self.idin201fSecondaryRt.setSingleStep(0.001)
        addTooltipInSpinBox(self.idin201fSecondaryRt, (0.0, 1.0))
        self.idin201fSecondaryRt.setValue(0.005)
        tempLayout.addWidget(QLabel("功因误差"))
        tempLayout.addWidget(self.idin201fSecondaryRt)

        tempLayout = QHBoxLayout()
        subLayout.addLayout(tempLayout)

        self.idin201fSecondaryFt = QDoubleSpinBox()
        self.idin201fSecondaryFt.setDecimals(2)
        self.idin201fSecondaryFt.setSingleStep(0.01)
        self.idin201fSecondaryFt.setSuffix("Hz")
        addTooltipInSpinBox(self.idin201fSecondaryFt, (0.0, 10.0))
        self.idin201fSecondaryFt.setValue(0.05)
        tempLayout.addWidget(QLabel("频率误差"))
        tempLayout.addWidget(self.idin201fSecondaryFt)

        self.idin201fSecondaryPt = QDoubleSpinBox()
        self.idin201fSecondaryPt.setDecimals(1)
        self.idin201fSecondaryPt.setSingleStep(0.1)
        self.idin201fSecondaryPt.setSuffix("%")
        addTooltipInSpinBox(self.idin201fSecondaryPt, (0.0, 10.0))
        self.idin201fSecondaryPt.setValue(0.5)
        tempLayout.addWidget(QLabel("有功误差"))
        tempLayout.addWidget(self.idin201fSecondaryPt)

        self.idin201fSecondaryQt = QDoubleSpinBox()
        self.idin201fSecondaryQt.setDecimals(1)
        self.idin201fSecondaryQt.setSingleStep(0.1)
        self.idin201fSecondaryQt.setSuffix("%")
        addTooltipInSpinBox(self.idin201fSecondaryQt, (0.0, 10.0))
        self.idin201fSecondaryQt.setValue(0.5)
        tempLayout.addWidget(QLabel("无功误差"))
        tempLayout.addWidget(self.idin201fSecondaryQt)

        for wdgt in (
            self.idin201fSecondaryIs,
            self.idin201fSecondaryUs,
            self.idin201fSecondaryRs,
            self.idin201fSecondaryIt,
            self.idin201fSecondaryUt,
            self.idin201fSecondaryRt,
            self.idin201fSecondaryFt,
            self.idin201fSecondaryPt,
            self.idin201fSecondaryQt,
        ):
            wdgt.valueChanged.connect(self.dealTab10UIPQF)

        self.idin201fSecondaryIsv = self.idin201fSecondaryIs.value()
        self.idin201fSecondaryUsv = self.idin201fSecondaryUs.value()
        self.idin201fSecondaryRsv = self.idin201fSecondaryRs.value()
        self.idin201fSecondaryPsv = (
            self.idin201fSecondaryIsv * self.idin201fSecondaryUsv * math.cos(math.radians(self.idin201fSecondaryRsv))
        )
        self.idin201fSecondaryQsv = (
            self.idin201fSecondaryIsv * self.idin201fSecondaryUsv * math.sin(math.radians(self.idin201fSecondaryRsv))
        )
        self.idin201fSecondarySsv = self.idin201fSecondaryIsv * self.idin201fSecondaryUsv

        splitter.addWidget(self.idin201fSecondaryGB)

        self.idin201fAdjustValueGB = QGroupBox("校正系数")
        self.idin201fAdjWs = []
        subLayout = QVBoxLayout(self.idin201fAdjustValueGB)
        for conf in ADJ_MEN_CONFS:
            tempLayout = QHBoxLayout()
            tempLayout.addWidget(QLabel(conf.name))
            sb = QSpinBox()
            addTooltipInSpinBox(sb, (0, 65535))
            self.idin201fAdjWs.append(sb)
            tempLayout.addWidget(sb)
            subLayout.addLayout(tempLayout)
        self.idin201fAdjustValueOverRangeFlags = False
        self.idin201fAdjReadBtn = QPushButton("读取")
        self.idin201fAdjReadBtn.clicked.connect(self.dealTab10Button)
        self.idin201fAdjWriteBtn = QPushButton("写入")
        self.idin201fAdjWriteBtn.clicked.connect(self.dealTab10Button)
        self.idin201fAdjRWBtns = (self.idin201fAdjReadBtn, self.idin201fAdjWriteBtn)
        tempLayout = QHBoxLayout()
        tempLayout.addWidget(self.idin201fAdjReadBtn)
        tempLayout.addWidget(self.idin201fAdjWriteBtn)
        subLayout.addLayout(tempLayout)
        self.idin201fAdjustValueGB.setLayout(subLayout)
        layout.addWidget(self.idin201fAdjustValueGB)

        idin201fSettingLayout = QVBoxLayout()
        self.idin201fSysParOverRangeFlags = False
        self.idin201fSysParaGB = QGroupBox("系统参数")
        self.idin201fSysParaWs = []
        subLayout = QVBoxLayout(self.idin201fSysParaGB)
        for conf in PARA_CONFS:
            tempLayout = QHBoxLayout()
            tempLayout.addWidget(QLabel(conf.name))
            if conf.type == "enum":
                w = QComboBox()
                w.addItems((i[1] for i in conf.switch))
            elif conf.ratio == 1:
                w = QSpinBox()
                addTooltipInSpinBox(w, (0, 65535))
            else:
                w = QDoubleSpinBox()
                w.setDecimals(int(math.log10(conf.ratio)))
                w.setSingleStep(1 / conf.ratio)
                addTooltipInSpinBox(w, (0.0, 6553.5))
            self.idin201fSysParaWs.append(w)
            tempLayout.addWidget(w)
            subLayout.addLayout(tempLayout)

        self.idin201fSysParaResetBtn = QPushButton("重置默认参数")
        self.idin201fSysParaResetBtn.clicked.connect(self.dealTab10Button)
        self.idin201fSysParaClearEngBtn = QPushButton("电度清零")
        self.idin201fSysParaClearEngBtn.clicked.connect(self.dealTab10Button)
        self.idin201fSysParaSetBtns = (self.idin201fSysParaResetBtn, self.idin201fSysParaClearEngBtn)
        tempLayout = QHBoxLayout()
        tempLayout.addWidget(self.idin201fSysParaResetBtn)
        tempLayout.addWidget(self.idin201fSysParaClearEngBtn)
        subLayout.addLayout(tempLayout)

        self.idin201fSysParaReadBtn = QPushButton("读取")
        self.idin201fSysParaReadBtn.clicked.connect(self.dealTab10Button)
        self.idin201fSysParaWriteBtn = QPushButton("写入")
        self.idin201fSysParaWriteBtn.clicked.connect(self.dealTab10Button)
        # self.idin201fSysParaWriteBtn.setDisabled(True)
        self.idin201fSysParaRWBtns = (self.idin201fSysParaReadBtn, self.idin201fSysParaWriteBtn)
        tempLayout = QHBoxLayout()
        tempLayout.addWidget(self.idin201fSysParaReadBtn)
        tempLayout.addWidget(self.idin201fSysParaWriteBtn)
        subLayout.addLayout(tempLayout)

        self.idin201fAdjCtlGB = QGroupBox("自动校正")
        subLayout = QVBoxLayout(self.idin201fAdjCtlGB)

        self.idin201fAdjStatusGB = QGroupBox("模式切换")
        tempLayout = QVBoxLayout(self.idin201fAdjStatusGB)
        self.idin201fAdjStatusOff = QRadioButton("退出自动校正")
        self.idin201fAdjStatusOff.setChecked(True)
        self.idin201fAdjStatusOff.clicked.connect(lambda: self.idin201fEnLvAdjMode(False))
        self.idin201fAdjStatusOn = QRadioButton("进入自动校正")
        self.idin201fAdjStatusOn.clicked.connect(lambda: self.idin201fEnLvAdjMode(True))
        self.idin201fAdjStatusBtns = (self.idin201fAdjStatusOn, self.idin201fAdjStatusOff)
        tempLayout.addWidget(self.idin201fAdjStatusOff)
        tempLayout.addWidget(self.idin201fAdjStatusOn)
        subLayout.addWidget(self.idin201fAdjStatusGB)

        self.idin201fAdjDealGB = QGroupBox("校正操作")
        tempLayout = QHBoxLayout(self.idin201fAdjDealGB)
        self.idin201fAdjAmpBtn = QPushButton("幅值校正")
        self.idin201fAdjAmpBtn.clicked.connect(self.dealTab10Button)
        self.idin201fAdjAngBtn = QPushButton("相位校正")
        self.idin201fAdjAngBtn.clicked.connect(self.dealTab10Button)
        self.idin201fAdjDealBtns = (self.idin201fAdjAmpBtn, self.idin201fAdjAngBtn)
        tempLayout.addWidget(self.idin201fAdjAmpBtn)
        tempLayout.addWidget(self.idin201fAdjAngBtn)
        subLayout.addWidget(self.idin201fAdjDealGB)

        self.idin201fAdjResultGB = QGroupBox("校正状态")
        self.idin201fAdjStatusMode = 0
        self.idin201fAdjStatusApm = 0
        self.idin201fAdjStatusAng = 0
        self.idin201fAdjResultApm = 0
        self.idin201fAdjResultAng = 0
        tempLayout = QGridLayout(self.idin201fAdjResultGB)
        tempLayout.addWidget(QLabel("校正模式:"), 0, 0)
        self.idin201fAdjStaMode = QLabel("已退出")
        self.idin201fAdjStaMode.setAlignment(Qt.AlignCenter)
        self.idin201fAdjStaMode.setAutoFillBackground(True)
        tempLayout.addWidget(self.idin201fAdjStaMode, 0, 1)

        tempLayout.addWidget(QLabel("幅值校正:"), 1, 0)
        self.idin201fAdjStaAmp = QLabel("进行中")
        self.idin201fAdjStaAmp.setAlignment(Qt.AlignCenter)
        self.idin201fAdjStaAmp.setAutoFillBackground(True)
        self.idin201fAdjRstAmp = QLabel("成功")
        self.idin201fAdjRstAmp.setAlignment(Qt.AlignCenter)
        self.idin201fAdjRstAmp.setAutoFillBackground(True)
        tempLayout.addWidget(self.idin201fAdjStaAmp, 1, 1)
        tempLayout.addWidget(self.idin201fAdjRstAmp, 1, 2)

        tempLayout.addWidget(QLabel("相位校正:"), 2, 0)
        self.idin201fAdjStaAng = QLabel("已完成")
        self.idin201fAdjStaAng.setAlignment(Qt.AlignCenter)
        self.idin201fAdjStaAng.setAutoFillBackground(True)
        self.idin201fAdjRstAng = QLabel("失败")
        self.idin201fAdjRstAng.setAlignment(Qt.AlignCenter)
        self.idin201fAdjRstAng.setAutoFillBackground(True)
        tempLayout.addWidget(self.idin201fAdjStaAng, 2, 1)
        tempLayout.addWidget(self.idin201fAdjRstAng, 2, 2)
        subLayout.addWidget(self.idin201fAdjResultGB)

        idin201fSettingLayout.addWidget(self.idin201fSysParaGB)
        idin201fSettingLayout.addWidget(self.idin201fAdjCtlGB)
        layout.addLayout(idin201fSettingLayout)

    def idin201fPreSetEngery(self, row, column):
        """IDIN201F设置电度 设置前准备"""

        def rollback():
            self.idin201fSecondaryMeaTableFreeze = None

        self.idin201fSecondaryMeaTableFreeze = (row, column)
        QTimer.singleShot(5000, rollback)

    def idin201fSetEngery(self, row, column):
        """IDIN201F设置电度 双击表格提交修改"""
        if column != 0:
            return
        item = self.idin201fSecondaryMeaTable.item(row, column)
        # self.logger.info(
        #     "double click in idin201fSecondaryMeaTable row {} column {} item text {}".format(row, column, item.text())
        # )
        self.idin201fSecondaryMeaTableFreeze = None
        sreMatch = RE_NUMBER.search(item.text())
        if sreMatch is None:
            return
        else:
            value = int(float(sreMatch.group()) * 10)
            data = (value // 65536, value % 65536)
        if row == 29:
            start = 0x10E9
        elif row == 30:
            start = 0x10EB
        elif row == 33:
            start = 0x10F1
        elif row == 34:
            start = 0x10F3
        self.sendQueuePack(funCode=0x10, start=start, data=data)

    def dealTab10Button(self):
        sender = self.sender()
        if sender in self.idin201fAdjRWBtns:  # 校正参数读写
            if sender is self.idin201fAdjReadBtn:
                self.idin201fAdjReadDeal()
            elif sender is self.idin201fAdjWriteBtn:
                self.idin201fAdjWriteDeal()
        if sender in self.idin201fSysParaSetBtns:  # 系统参数控制
            if sender is self.idin201fSysParaResetBtn:  # 参数重置
                self.sendQueuePack(funCode=0x10, start=0x0194, data=(0xFFFF,))
                for w in self.idin201fSysParaWs:
                    w.setStyleSheet("color:#ff0000;")
                QTimer.singleShot(1500, self.idin201fSysParReadDeal)
            elif sender is self.idin201fSysParaClearEngBtn:  # 电度清零
                self.sendQueuePack(funCode=0x10, start=0x0195, data=(0xFFFF,))
        elif sender in self.idin201fSysParaRWBtns:  # 系统参数读写
            if sender is self.idin201fSysParaReadBtn:
                self.idin201fSysParReadDeal()
            elif sender is self.idin201fSysParaWriteBtn:
                self.idin201fSysParWriteDeal()
        # elif sender in self.idin201fAdjStatusBtns:  # 进退校正状态指令下发
        #     idx = self.idin201fAdjStatusBtns.index(sender)
        #     self.logger.info("self.idin201fAdjStatusBtns index {}".format(idx))
        elif sender in self.idin201fAdjDealBtns:  # 自动校正指令下发(幅值\相位)
            if sender is self.idin201fAdjAmpBtn:  # 幅值校正命令下发
                self.idin201fAdjAmpDeal()
            elif sender is self.idin201fAdjAngBtn:  # 角度校正命令下发
                self.idin201fAdjAngDeal()

    def idin201fAdjReadDeal(self):
        """
        读取IDIN201F校正参数
        """
        if self.serRecvDir.get(0x01F4) is None:
            return
        overRangeList = []
        data = [bytes2Int(self.serRecvDir.get(0x01F4 + i)) for i in range(15)]
        for i in range(15):
            wdgt = self.idin201fAdjWs[i]
            showData = data[i]
            showName = ADJ_MEN_CONFS[i].name
            wdgt.setValue(showData)
            maximum = wdgt.maximum()
            minimum = wdgt.minimum()
            if showData > maximum or showData < minimum:
                overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format(showName, showData, minimum, maximum))
                wdgt.setStyleSheet("color:#ff0000;")
            else:
                wdgt.setStyleSheet("")

        if len(overRangeList) > 0 and not self.idin201fAdjustValueOverRangeFlags:
            self.idin201fAdjustValueOverRangeFlags = True
            messageBox = ScrollMessageBox(msgList=overRangeList, width=250, parent=self)
            messageBox.setWindowTitle("IDIN201F 校正参数超范围")
            messageBox.exec_()
            self.idin201fAdjustValueOverRangeFlags = False

    def idin201fAdjWriteDeal(self):
        """
        写入IDIN201F校正参数
        """
        value = [w.value() for w in self.idin201fAdjWs]
        for i, w in enumerate(self.idin201fAdjWs):
            w.setStyleSheet("color:#ff0000;")
        self.sendQueuePack(funCode=0x10, start=0x01F4, data=value)
        QTimer.singleShot(1500, self.idin201fAdjReadDeal)

    def idin201fSysParReadDeal(self):
        """
        读取IDIN201F系统参数
        """
        if self.serRecvDir.get(0x00C8) is None:
            return
        overRangeList = []
        for conf, wdgt in zip(PARA_CONFS, self.idin201fSysParaWs):
            showData = bytes2Int(self.serRecvDir.get(conf.addrs[0]))
            showName = conf.name
            if isinstance(wdgt, QDoubleSpinBox):
                showData = showData / conf.ratio
                wdgt.setValue(showData)
                maximum = wdgt.maximum()
                minimum = wdgt.minimum()
                if showData > maximum or showData < minimum:
                    overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format(showName, showData, minimum, maximum))
                    wdgt.setStyleSheet("color:#ff0000;")
                else:
                    wdgt.setStyleSheet("")
            elif isinstance(wdgt, QSpinBox):
                wdgt.setValue(showData)
                maximum = wdgt.maximum()
                minimum = wdgt.minimum()
                if showData > maximum or showData < minimum:
                    overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format(showName, showData, minimum, maximum))
                    wdgt.setStyleSheet("color:#ff0000;")
                else:
                    wdgt.setStyleSheet("")
            elif isinstance(wdgt, QComboBox):
                if showData not in [i[0] for i in conf.switch]:
                    overRangeList.append("{} 读取值 {} 范围 {}".format(showName, showData, conf.switch))
                    wdgt.setStyleSheet("color:#ff0000;")
                else:
                    wdgt.setCurrentIndex([i[0] for i in conf.switch].index(showData))
                    wdgt.setStyleSheet("")

        if len(overRangeList) > 0 and not self.idin201fSysParOverRangeFlags:
            self.idin201fSysParOverRangeFlags = True
            messageBox = ScrollMessageBox(msgList=overRangeList, width=400, parent=self)
            messageBox.setWindowTitle("IDIN201F 系统参数超范围")
            messageBox.exec_()
            self.idin201fSysParOverRangeFlags = False

    def idin201fSysParWriteDeal(self):
        """
        写入IDIN201F系统参数
        """
        data = [bytes2Int(self.serRecvDir.get(0x00C8 + i)) for i in range(16)]
        for conf, wdgt in zip(PARA_CONFS, self.idin201fSysParaWs):
            if isinstance(wdgt, QDoubleSpinBox):
                value = int(wdgt.value() * conf.ratio)
            elif isinstance(wdgt, QSpinBox):
                value = wdgt.value()
            elif isinstance(wdgt, QComboBox):
                value = conf.switch[wdgt.currentIndex()][0]
            data[conf.addrs[0] - 0x00C8] = value
            wdgt.setStyleSheet("color:#ff0000;")
        self.sendQueuePack(funCode=0x10, start=0x00C8, data=data[:7])
        self.sendQueuePack(funCode=0x10, start=0x00D6, data=data[-2:])
        QTimer.singleShot(1500, self.idin201fSysParReadDeal)

    def dealTab10UIPQF(self):
        """
        处理基准值 误差设定值变化
        """
        ichange, uchange, rchange = False, False, False
        sender = self.sender()
        if sender is self.idin201fSecondaryIs:
            ichange = True
        if sender is self.idin201fSecondaryUs:
            uchange = True
        if sender is self.idin201fSecondaryRs:
            rchange = True
        if any((ichange, uchange, rchange)):
            self.idin201fSecondaryIsv = self.idin201fSecondaryIs.value()
            self.idin201fSecondaryUsv = self.idin201fSecondaryUs.value()
            self.idin201fSecondaryRsv = self.idin201fSecondaryRs.value()
            self.idin201fSecondarySsv = self.idin201fSecondaryIsv * self.idin201fSecondaryUsv
            self.idin201fSecondaryPsv = self.idin201fSecondarySsv * math.cos(math.radians(self.idin201fSecondaryRsv))
            self.idin201fSecondaryQsv = self.idin201fSecondarySsv * math.sin(math.radians(self.idin201fSecondaryRsv))
            for j in (17, 18, 19):
                self.idin201fSecondaryMeaTable.item(j, 1).setText("{:.3f} W".format(self.idin201fSecondaryPsv))
            self.idin201fSecondaryMeaTable.item(20, 1).setText("{:.3f} W".format(self.idin201fSecondaryPsv * 3))
            for j in (21, 22, 23):
                self.idin201fSecondaryMeaTable.item(j, 1).setText("{:.3f} Var".format(self.idin201fSecondaryQsv))
            self.idin201fSecondaryMeaTable.item(24, 1).setText("{:.3f} Var".format(self.idin201fSecondaryQsv * 3))
            for j in (25, 26, 27):
                self.idin201fSecondaryMeaTable.item(j, 1).setText("{:.3f} VA".format(self.idin201fSecondarySsv))
            self.idin201fSecondaryMeaTable.item(28, 1).setText("{:.3f} VA".format(self.idin201fSecondarySsv * 3))
            if ichange:
                for j in (0, 1, 2, 3):
                    self.idin201fSecondaryMeaTable.item(j, 1).setText("{:.2f} A".format(self.idin201fSecondaryIsv))
            if uchange:
                for j in (4, 5, 6, 7):
                    self.idin201fSecondaryMeaTable.item(j, 1).setText("{:.2f} V".format(self.idin201fSecondaryUsv))
                for j in (8, 9, 10, 11):
                    self.idin201fSecondaryMeaTable.item(j, 1).setText(
                        "{:.2f} V".format(self.idin201fSecondaryUsv * 3 ** 0.5)
                    )
            if rchange:
                for j in (12, 13, 14, 15):
                    self.idin201fSecondaryMeaTable.item(j, 1).setText(
                        "{:.3f}".format(math.cos(math.radians(self.idin201fSecondaryRsv)))
                    )

    def updateIDIN201FMeaTable(self):
        # 一次值
        if all((d in self.serRecvDir.keys() for d in range(0x0000, 0x0055 + 1))):
            for i, conf in enumerate(PRIMARY_MEA_CONFS):
                dataByte = b"".join((self.serRecvDir.get(addr) for addr in conf.addrs))
                dataValue = bytes2Float32(dataByte, module="ABCD") / conf.ratio
                if abs(dataValue) <= 10 ** 6:
                    dataStr = "{:.3f} {}".format(dataValue, conf.unit)
                else:
                    if dataValue > 0:
                        dataStr = ">1000000"
                    else:
                        dataStr = "<-1000000"
                # self.logger.info("{} dataByte {} dataValue {} dataStr {}".format(conf.name, bytesPuttyPrint(dataByte), dataValue, dataStr))
                self.idin201fPrimaryMeaTable.item(i, 0).setText(dataStr)
        # 二次值
        if all((d in self.serRecvDir.keys() for d in range(0x10CC, 0x10F8 + 1))):
            self.idin201fSecondaryMeaTable.blockSignals(True)
            for i, conf in enumerate(SECONDARY_MEA_CONFS):
                dataByte = b"".join((self.serRecvDir.get(addr) for addr in conf.addrs))
                if conf.type == "float32":
                    dataValue = bytes2Float32(dataByte) / conf.ratio
                else:
                    if conf.type.startswith("int"):
                        signed = True
                    else:
                        signed = False
                    dataValue = bytes2Int(dataByte, signed=signed) / conf.ratio
                dataStr = "{0:{1.format}} {1.unit}".format(dataValue, conf)
                if (i, 0) != self.idin201fSecondaryMeaTableFreeze:
                    self.idin201fSecondaryMeaTable.item(i, 0).setText(dataStr)

                if i in (0, 1, 2, 3):
                    sdata = self.idin201fSecondaryIsv
                    toleranceBase = self.idin201fSecondaryIt.value()
                elif i in (4, 5, 6, 7):
                    sdata = self.idin201fSecondaryUsv
                    toleranceBase = self.idin201fSecondaryUt.value()
                elif i in (8, 9, 10, 11):
                    sdata = self.idin201fSecondaryUsv * 3 ** 0.5
                    toleranceBase = self.idin201fSecondaryUt.value()
                elif i in (12, 13, 14, 15):
                    sdata = math.cos(math.radians(self.idin201fSecondaryRsv))
                    toleranceBase = self.idin201fSecondaryRt.value()
                elif i == 16:
                    toleranceBase = self.idin201fSecondaryFt.value()
                    sdata = 50
                elif i in (17, 18, 19, 20):
                    sdata = self.idin201fSecondaryPsv
                    if i == 20:
                        sdata *= 3
                    toleranceBase = self.idin201fSecondaryPt.value()
                elif i in (21, 22, 23, 24):
                    sdata = self.idin201fSecondaryQsv
                    if i == 24:
                        sdata *= 3
                    toleranceBase = self.idin201fSecondaryQt.value()
                elif i in (25, 26, 27, 28):
                    sdata = self.idin201fSecondarySsv
                    if i == 28:
                        sdata *= 3
                    toleranceBase = 1

                if conf.toleranceType is not None:
                    self.idin201fSecondaryMeaTable.item(i, 1).setText("{0:{1.format}} {1.unit}".format(sdata, conf))

                if conf.toleranceType == "percent":
                    if sdata > 0 and sdata <= 10 ** -2:
                        sdata = 10 ** -5
                    if sdata < 0 and sdata >= -(10 ** -2):
                        sdata = -(10 ** -5)
                    tolerance = (dataValue - sdata) / sdata
                    if tolerance <= 100:
                        self.idin201fSecondaryMeaTable.item(i, 2).setText("{:+.2%}".format(tolerance))
                    else:
                        self.idin201fSecondaryMeaTable.item(i, 2).setText(">10000%")
                    if abs(tolerance) * 100 <= toleranceBase:
                        self.idin201fSecondaryMeaTable.item(i, 2).setBackground(QColor("#00FF00"))
                    else:
                        self.idin201fSecondaryMeaTable.item(i, 2).setBackground(QColor("#FF0000"))
                elif conf.toleranceType == "absolute":
                    tolerance = round((dataValue - sdata), 4)
                    self.idin201fSecondaryMeaTable.item(i, 2).setText(
                        "{0:{1.format}} {1.unit}".format(tolerance, conf)
                    )
                    if abs(tolerance) <= toleranceBase:
                        self.idin201fSecondaryMeaTable.item(i, 2).setBackground(QColor("#00FF00"))
                    else:
                        self.idin201fSecondaryMeaTable.item(i, 2).setBackground(QColor("#FF0000"))
            self.idin201fSecondaryMeaTable.blockSignals(False)

    def idin201fAdjStatusRead(self):
        """IDIN201F校正状态读取"""
        if self.serRecvDir.get(0x1051) is None:
            return
        status = bytes2Int(self.serRecvDir.get(0x1051))
        self.idin201fAdjStatusMode = (status >> 0) & 1
        self.idin201fAdjStatusApm = (status >> 1) & 1
        self.idin201fAdjStatusAng = (status >> 2) & 1
        self.idin201fAdjResultApm = (status >> 3) & 1
        self.idin201fAdjResultAng = (status >> 4) & 1

        if self.idin201fAdjStatusMode == 0:  # 自动校正模式
            self.idin201fAdjStaMode.setText("已退出")
            self.idin201fAdjStaMode.setStyleSheet("background-color:#00ff00;")
            self.idin201fAdjStatusOff.setChecked(True)
            self.idin201fAdjAmpBtn.setDisabled(True)
            self.idin201fAdjAngBtn.setDisabled(True)
        else:
            self.idin201fAdjStaMode.setText("工作中")
            self.idin201fAdjStaMode.setStyleSheet("background-color:#ff0000;")
            self.idin201fAdjStatusOn.setChecked(True)
            self.idin201fAdjAmpBtn.setDisabled(False)
            self.idin201fAdjAngBtn.setDisabled(False)

        if self.idin201fAdjStatusApm == 0:  # 幅值校正状态
            self.idin201fAdjStaAmp.setText("已结束")
            self.idin201fAdjStaAmp.setStyleSheet("background-color:#00ff00;")
        else:
            self.idin201fAdjStaAmp.setText("进行中")
            self.idin201fAdjStaAmp.setStyleSheet("background-color:#ff0000;")

        if self.idin201fAdjStatusAng == 0:  # 角度校正状态
            self.idin201fAdjStaAng.setText("已结束")
            self.idin201fAdjStaAng.setStyleSheet("background-color:#00ff00;")
        else:
            self.idin201fAdjStaAng.setText("进行中")
            self.idin201fAdjStaAng.setStyleSheet("background-color:#ff0000;")

        if self.idin201fAdjResultApm == 0:  # 幅值校正结果
            self.idin201fAdjRstAmp.setText("成功")
            self.idin201fAdjRstAmp.setStyleSheet("background-color:#00ff00;")
        else:
            self.idin201fAdjRstAmp.setText("失败")
            self.idin201fAdjRstAmp.setStyleSheet("background-color:#ff0000;")

        if self.idin201fAdjResultAng == 0:  # 角度校正结果
            self.idin201fAdjRstAng.setText("成功")
            self.idin201fAdjRstAng.setStyleSheet("background-color:#00ff00;")
        else:
            self.idin201fAdjRstAng.setText("失败")
            self.idin201fAdjRstAng.setStyleSheet("background-color:#ff0000;")

    def idin201fEnLvAdjMode(self, mode: bool):
        """
        IDIN201F进退自动校正模式
        """
        if mode is True:
            self.sendQueuePack(funCode=0x10, start=0x1000, data=(0xFF00,))
        elif mode is False:
            self.sendQueuePack(funCode=0x10, start=0x1000, data=(0x0000,))

    def idin201fAdjAmpDeal(self):
        """IDIN201F幅值校正命令下发"""
        self.sendQueuePack(funCode=0x10, start=0x1050, data=(0x0001,))

    def idin201fAdjAngDeal(self):
        """IDIN201F幅值校正命令下发"""
        self.sendQueuePack(funCode=0x10, start=0x1050, data=(0x0002,))

    def updateBySerialRecvBuff(self, changeAddrList):
        """数据缓存发生变化时需要刷新部分"""
        if any((i in changeAddrList for i in range(0x0000, 0x0055 + 1))) or any(
            (i in changeAddrList for i in range(0x10CC, 0x10F8 + 1))
        ):
            self.updateIDIN201FMeaTable()
        if any((i in changeAddrList for i in range(0x01F4, 0x202 + 1))):
            self.idin201fAdjReadDeal()
        if any((i in changeAddrList for i in range(0x00C8, 0x00D7 + 1))):
            self.idin201fSysParReadDeal()
        self.idin201fAdjStatusRead()

    def updateByTimer(self, timeout=1):
        pass

    def updateUnconditional(self):
        """无条件刷新界面显示部分"""
        pass

    def updateWhole(self):
        """更新全体显示构件"""
        self.idin201fAdjReadDeal()
        self.idin201fSysParReadDeal()
